
using System;
using System.Collections.Generic;
using System.Text;
using static GameLogic.SocketDefinition;

namespace GameLogic
{
    #region API cmd定义
    public enum API_ID_BASE : uint
    {
        /// <summary>
        /// 客户端暂定0xFFFFFFFF表示无意义的消息ID，用于默认值设定，如服务器规定要使用该ID了，则需要配合修改
        /// </summary>
        NONE = 0xFFFFFFFF,
        //心跳
        HEARTBEAT = 0x0,
        //反馈-心跳
        HEARTBEAT_RESPONSE = GAME_REF + HEARTBEAT,
        //创建连接
        BUILD_LINK = 0x600,
        //获取匿名访问ID
        GET_ANONYMOUS_ID = BS_MSG_AGENT + 0x1,
        //反馈-获取匿名访问ID
        ON_GET_ANONYMOUS_ID = GAME_REF + GET_ANONYMOUS_ID,
        //登录(老)
        [Obsolete("请使用SOCKET_LOGIN，老包废弃")]
        OLD_SOCKET_LOGIN = BS_MSG_AGENT + 0x3,
        //反馈-登录(老)
        [Obsolete("请使用ON_SOCKET_LOGIN，老包废弃")]
        ON_OLD_SOCKET_LOGIN = GAME_REF + OLD_SOCKET_LOGIN,
        //登出
        SIGN_OUT = BS_MSG_AGENT + 0x4,
        //反馈-登出
        ON_SIGN_OUT = GAME_REF + SIGN_OUT,
        // 服务器错误
        SERVICE_ERROR = BS_MSG_AGENT + 0x5,
        //登录（新）
        SOCKET_LOGIN = BS_MSG_AGENT + 0x6,
        //反馈-登录（新）
        ON_SOCKET_LOGIN = GAME_REF + SOCKET_LOGIN,
        //微信code授权信息
        WECHAT_CODE_AUTHORIZE = BS_MSG_AGENT + 0x8,
        //反馈-微信code授权信息
        ON_WECHAT_CODE_AUTHORIZE = GAME_REF + WECHAT_CODE_AUTHORIZE
    }
    #endregion

    #region 公共包，有包头、后缀等
    /// <summary>
    /// 老包头，用于send时
    /// 区分发送和接收，继承不同的类，条理清晰一些
    /// </summary>
    public class OldHeadSend : SendPack
    {
        public static uint Size = 12;
        protected override uint GetCmdID() => (uint)API_ID_BASE.NONE;

        public ushort tag = TAG_BS;
        public ushort bodylen;
        public byte checkcode = HEAD_PARAM_ZERO;
        public byte messagever = (byte)MESSAGE_VER_TYPE.NO;
        public uint cmdId;
        public ushort reverved = (ushort)HEAD_PARAM_ZERO;

        public override List<(TypeInfo type, string propertyName)> PropertySequence => new()
        {
            (D_TYPE.WORD, nameof(tag)),
            (D_TYPE.WORD, nameof(bodylen)),
            (D_TYPE.WORD8, nameof(checkcode)),
            (D_TYPE.WORD8, nameof(messagever)),
            (D_TYPE.DWORD, nameof(cmdId)),
            (D_TYPE.WORD, nameof(reverved))
        };
    }
    /// <summary>
    /// 老包头，用于receive时
    /// 区分发送和接收，继承不同的类，条理清晰一些
    /// </summary>
    public class OldHeadReceive : ReceivePack
    {
        public static uint Size = 12;

        public ushort tag = TAG_BS;
        public ushort bodylen;
        public byte checkcode = HEAD_PARAM_ZERO;
        public byte messagever = (byte)MESSAGE_VER_TYPE.NO;
        public uint cmdId;
        public ushort reverved = (ushort)HEAD_PARAM_ZERO;

        public override List<(TypeInfo type, string propertyName)> PropertySequence => new()
        {
            (D_TYPE.WORD, nameof(tag)),
            (D_TYPE.WORD, nameof(bodylen)),
            (D_TYPE.WORD8, nameof(checkcode)),
            (D_TYPE.WORD8, nameof(messagever)),
            (D_TYPE.DWORD, nameof(cmdId)),
            (D_TYPE.WORD, nameof(reverved))
        };
    }

    /// <summary>
    /// 新包头，用于send时
    /// 区分发送和接收，继承不同的类，条理清晰一些
    /// </summary>
    public class NewHeadSend : SendPack
    {
        public static uint Size = 24;

        protected override uint GetCmdID() => (uint)API_ID_BASE.NONE;

        public uint dwMagic = HEAD_PARAM_ZERO;
        public uint dwSerial = HEAD_PARAM_ZERO;
        public ushort wOrigine = (ushort)ORIGIN_TYPE.CLTH5;
        public ushort wReserve = HEAD_PARAM_ZERO;
        public uint cmdId;
        public int dwParam = HEAD_PARAM_ZERO;
        public uint bodylen;
        public override List<(TypeInfo type, string propertyName)> PropertySequence => new()
        {
            (D_TYPE.DWORD, nameof(dwMagic)),
            (D_TYPE.DWORD, nameof(dwSerial)),
            (D_TYPE.WORD, nameof(wOrigine)),
            (D_TYPE.WORD, nameof(wReserve)),
            (D_TYPE.DWORD, nameof(cmdId)),
            (D_TYPE.INT, nameof(dwParam)),
            (D_TYPE.DWORD, nameof(bodylen))
        };
    }

    /// <summary>
    /// 新包头，用于receive时
    /// 区分发送和接收，继承不同的类，条理清晰一些
    /// </summary>
    public class NewHeadReceive : ReceivePack
    {
        public static uint Size = 24;

        public uint dwMagic = HEAD_PARAM_ZERO;
        public uint dwSerial = HEAD_PARAM_ZERO;
        public ushort wOrigine = (ushort)ORIGIN_TYPE.CLTH5;
        public ushort wReserve = HEAD_PARAM_ZERO;
        public uint cmdId;
        public int dwParam = HEAD_PARAM_ZERO;
        public uint bodylen;
        public override List<(TypeInfo type, string propertyName)> PropertySequence => new()
        {
            (D_TYPE.DWORD, nameof(dwMagic)),
            (D_TYPE.DWORD, nameof(dwSerial)),
            (D_TYPE.WORD, nameof(wOrigine)),
            (D_TYPE.WORD, nameof(wReserve)),
            (D_TYPE.DWORD, nameof(cmdId)),
            (D_TYPE.INT, nameof(dwParam)),
            (D_TYPE.DWORD, nameof(bodylen))
        };
    }

    /// <summary>
    /// 后缀数据索引，规定了后缀数据的偏移量和大小
    /// </summary>
    public class SuffixIdxSend : SendPack
    {
        protected override uint GetCmdID() => (uint)API_ID_BASE.NONE;

        public uint offset;
        public uint size;
        public override List<(TypeInfo type, string propertyName)> PropertySequence => new()
        {
            (D_TYPE.DWORD, nameof(offset)),
            (D_TYPE.DWORD, nameof(size))
        };
    }

    /// <summary>
    /// 后缀数据索引，规定了后缀数据的偏移量和大小
    /// </summary>
    public class SuffixIdxReceive : ReceivePack
    {
        public uint offset;
        public uint size;
        public override List<(TypeInfo type, string propertyName)> PropertySequence => new()
        {
            (D_TYPE.DWORD, nameof(offset)),
            (D_TYPE.DWORD, nameof(size))
        };
    }

    #endregion

    #region 发送的包
    public class HeartbeatPacket : SendPack
    {
        protected override uint GetCmdID() => (uint)API_ID_BASE.HEARTBEAT;

        public HeartbeatPacket() => HasNewHead = false;
    }

    public class BuildLinkPacket : SendPack
    {
        protected override uint GetCmdID() => (uint)API_ID_BASE.BUILD_LINK;
    }

    public class GetAnonymousIDPacket : SendPack
    {
        protected override uint GetCmdID() => (uint)API_ID_BASE.GET_ANONYMOUS_ID;

        public uint unityVersion;
        public uint time;

        public override List<(TypeInfo type, string propertyName)> PropertySequence => new()
        {
            (D_TYPE.DWORD, nameof(unityVersion)),
            (D_TYPE.DWORD, nameof(time))
        };
    }

    [Obsolete("请使用SocketLoginPacket，老包废弃")]
    public class OldSocketLoginPacket : SendPack
    {
        protected override uint GetCmdID() => (uint)API_ID_BASE.OLD_SOCKET_LOGIN;

        /// <summary>
        /// 账号类型：
        /// 0:UserName; 1:UserID; 2:Mobile; 3:Ticket; 4:QQ
        /// 5:Sina; 6:Visitor; 7:Weixin; 8:Baidu; 9:360
        /// 10:Huawei; 11:Wxpub; 12:Oppo; 13:Vivo; 14:Mi
        /// 15:Yyb; 20:手机短信; 21:手机票据(默认0)
        /// 22:本机号码一键登录
        /// 103:票据验证断线重连登录
        /// 203:票据验证登录
        /// </summary>
        public uint accountType;

        /// <summary>
        /// 用户登录名称
        /// </summary>
        public string username;

        /// <summary>
        /// 用户登录密码
        /// </summary>
        public string password;

        /// <summary>
        /// 登录标识
        /// </summary>
        public uint flag;

        /// <summary>
        /// MAC地址1
        /// </summary>
        public string mac1;

        /// <summary>
        /// MAC地址2
        /// </summary>
        public string mac2;

        /// <summary>
        /// 传输ID
        /// </summary>
        public string tranferid;

        /// <summary>
        /// 平台ID
        /// </summary>
        public string platform;

        /// <summary>
        /// 客户端版本号(格式：8.0.0→080000)
        /// </summary>
        public uint appVersion;

        /// <summary>
        /// 客户端包ID
        /// </summary>
        public uint clientMsgID = (uint)ORIGIN_TYPE.CLTH5;

        /// <summary>
        /// 游戏ID
        /// </summary>
        public uint gameID = 0;

        /// <summary>
        /// 客户端本机IP(局域网IP)
        /// </summary>
        public string privateIP = null;

        /// <summary>
        /// 客户端外网IP(互联网出口IP)
        /// </summary>
        public string publicIP = null;

        /// <summary>
        /// 设备型号
        /// </summary>
        public string devicename;

        /// <summary>
        /// 设备名称(应用市场)
        /// </summary>
        public string market;

        /// <summary>
        /// 操作系统
        /// </summary>
        public uint os;

        /// <summary>
        /// JS版本号(格式：0.13.7→001307)
        /// </summary>
        public uint jsVersion;

        /// <summary>
        /// 保留字1
        /// </summary>
        public uint reserve1 = 0;

        /// <summary>
        /// 拓展账号类型字段
        /// </summary>
        public uint extraAccountType = 0;

        /// <summary>
        /// 设备ID
        /// </summary>
        public string deviceID = null;

        /// <summary>
        /// JS地址(热更版本提醒使用)
        /// </summary>
        public string jsPath = null;

        /// <summary>
        /// 占位符，保留字段
        /// </summary>
        public uint placeholder1 = 0;
        public uint placeholder2 = 0;
        public uint placeholder3 = 0;
        public uint placeholder4 = 0;

        /// <summary>
        /// 额外登录数据长度和偏移量
        /// </summary>
        public SuffixIdxSend extraParamSize = new SuffixIdxSend();

        /// <summary>
        /// 数据长度和偏移量（占位，未使用）
        /// </summary>
        public SuffixIdxSend placeholderSize = new SuffixIdxSend();

        private string _extraParam;
        /// <summary>
        /// 额外登录数据内容
        /// </summary>
        public string extraParam
        {
            get => _extraParam;
            set
            {
                if (_extraParam != value)
                {
                    _extraParam = value;
                    extraParamSize.offset = (uint)APILength + NewHeadSend.Size + OldHeadSend.Size;
                    extraParamSize.size = (uint)(value?.Length ?? 0);
                }
            }
        }

        public override List<(TypeInfo type, string propertyName)> PropertySequence => new()
        {
            (D_TYPE.DWORD, nameof(accountType)),
            (D_TYPE.TCHAR64, nameof(username)),
            (D_TYPE.TCHAR32, nameof(password)),
            (D_TYPE.DWORD, nameof(flag)),
            (D_TYPE.TCHAR24, nameof(mac1)),
            (D_TYPE.DWORD, nameof(tranferid)),
            (D_TYPE.DWORD, nameof(platform)),
            (D_TYPE.DWORD, nameof(appVersion)),
            (D_TYPE.DWORD, nameof(clientMsgID)),
            (D_TYPE.DWORD, nameof(gameID)),
            (D_TYPE.DWORD, nameof(privateIP)),
            (D_TYPE.DWORD, nameof(publicIP)),
            (D_TYPE.TCHAR128, nameof(devicename)),
            (D_TYPE.TCHAR128, nameof(market)),
            (D_TYPE.DWORD, nameof(os)),
            (D_TYPE.DWORD, nameof(jsVersion)),
            (D_TYPE.DWORD, nameof(reserve1)),
            (D_TYPE.DWORD, nameof(extraAccountType)),
            (D_TYPE.TCHAR32, nameof(deviceID)),
            (D_TYPE.TCHAR32, nameof(jsPath)), 
            (D_TYPE.DWORD, nameof(placeholder1)),
            (D_TYPE.DWORD, nameof(placeholder2)),
            (D_TYPE.DWORD, nameof(placeholder3)),
            (D_TYPE.DWORD, nameof(placeholder4)),
            (extraParamSize, nameof(extraParamSize)),
            (placeholderSize, nameof(placeholderSize))
        };

        protected override byte[] Write()
        {
            byte[] data = base.Write();
            byte[] extraBytes = Array.Empty<byte>();
            if (extraParam != null && extraParam.Length > 0)
            {
                extraBytes = Encoding.GetEncoding(DEFAULT_ENCODING_CODEPAGE).GetBytes(extraParam); 
            }
            
            return GameUtility.CombineBytes(data, extraBytes);
        }
    }

    public class SignOutPacket : SendPack
    {
        public uint userID;
        public string nickName;
        public uint reserve = 0;

        protected override uint GetCmdID() => (uint)API_ID_BASE.SIGN_OUT;
        public override List<(TypeInfo type, string propertyName)> PropertySequence => new()
        {
            (D_TYPE.DWORD, nameof(userID)),
            (D_TYPE.DWORD, nameof(reserve)),
            (D_TYPE.TCHAR32, nameof(nickName))
        };
    }

    public class SocketLoginPacket : SendPack
    {
        protected override uint GetCmdID() => (uint)API_ID_BASE.SOCKET_LOGIN;

        public uint accountType;
        public string loginID;
        public string password;
        public string nickname = "";
        public string headUrl = "";
        public string inviteCode;
        public string mac1;
        public string mac2;
        public uint loginType;
        public uint platform;
        public uint appVersion;
        public uint unityVersion;
        public string unityResPath;
        public string appStr;
        public uint clientMsgID = (uint)ORIGIN_TYPE.CLTH5;
        public uint localIP = 0;
        public string devicename;
        public string market;
        public uint os;
        public string deviceID;
        public string timeStamp;
        public uint reserve1 = 0;
        public uint extensionLoginType = 0;
        public uint reserve3 = 0;

        public SuffixIdxSend suffixIdx = new SuffixIdxSend();
        public SuffixIdxSend suffixIdx2 = new SuffixIdxSend();

        private string _extraParam = "";
        private string _extraParam2 = "";

        public SocketLoginPacket(string extraParam = null, string extraParam2 = null)
        {
            this.extraParam = extraParam;
            this.extraParam2 = extraParam2;
        }

        public string extraParam
        {
            get => _extraParam;
            set
            {
                if (_extraParam != value)
                {
                    _extraParam = value;
                    suffixIdx.offset = (uint)APILength + NewHeadSend.Size + OldHeadSend.Size;
                    suffixIdx.size = (uint)(value?.Length ?? 0);
                }
            }
        }

        public string extraParam2
        {
            get => _extraParam2;
            set
            {
                if (_extraParam2 != value)
                {
                    _extraParam2 = value;
                    suffixIdx2.offset = (uint)APILength + NewHeadSend.Size + OldHeadSend.Size + suffixIdx.size;
                    suffixIdx2.size = (uint)(value?.Length ?? 0);
                }
            }
        }

        protected override byte[] Write()
        {
            byte[] data = base.Write();
            byte[] extraBytes = Array.Empty<byte>();
            byte[] extra2Bytes = Array.Empty<byte>();
            if (extraParam != null && extraParam.Length > 0)
            {
                extraBytes = Encoding.GetEncoding(DEFAULT_ENCODING_CODEPAGE).GetBytes(extraParam);
            }
            if (extraParam2 != null && extraParam2.Length > 0)
            {
                extra2Bytes = Encoding.GetEncoding(DEFAULT_ENCODING_CODEPAGE).GetBytes(extraParam2);
            }
            return GameUtility.CombineBytes(data, extraBytes, extra2Bytes);
        }

        public override List<(TypeInfo type, string propertyName)> PropertySequence => new()
        {
            (D_TYPE.DWORD, nameof(accountType)),
            (D_TYPE.TCHAR64, nameof(loginID)),
            (D_TYPE.TCHAR32, nameof(password)),
            (D_TYPE.TCHAR32, nameof(nickname)),
            (D_TYPE.TCHAR256, nameof(headUrl)),
            (D_TYPE.TCHAR32, nameof(inviteCode)),
            (D_TYPE.TCHAR24, nameof(mac1)),
            (D_TYPE.TCHAR24, nameof(mac2)),
            (D_TYPE.DWORD, nameof(loginType)),
            (D_TYPE.DWORD, nameof(platform)),
            (D_TYPE.DWORD, nameof(appVersion)),
            (D_TYPE.DWORD, nameof(unityVersion)),
            (D_TYPE.TCHAR32, nameof(unityResPath)),
            (D_TYPE.TCHAR16, nameof(appStr)),
            (D_TYPE.DWORD, nameof(clientMsgID)),
            (D_TYPE.DWORD, nameof(localIP)),
            (D_TYPE.TCHAR128, nameof(devicename)),
            (D_TYPE.TCHAR128, nameof(market)),
            (D_TYPE.DWORD, nameof(os)),
            (D_TYPE.TCHAR32, nameof(deviceID)),
            (D_TYPE.TCHAR16, nameof(timeStamp)),
            (D_TYPE.DWORD, nameof(reserve1)),
            (D_TYPE.DWORD, nameof(extensionLoginType)),
            (D_TYPE.DWORD, nameof(reserve3)),
            (suffixIdx, nameof(suffixIdx)),
            (suffixIdx2, nameof(suffixIdx2))
        };
    }
    public class WechatCodeAuthorizePacket : SendPack
    {
        public string appStr;
        public SuffixIdxSend suf = new ();
        public uint type = 0;  // 0:微信小游戏  1:微信网页

        private string _code;
        public string Code
        {
            get => _code;
            set
            {
                if (_code != value)
                {
                    _code = value;
                    suf.offset = (uint)APILength + NewHeadSend.Size + OldHeadSend.Size;
                    suf.size = (uint)(value?.Length ?? 0);
                }
            }
        }

        protected override uint GetCmdID() => (uint)API_ID_BASE.SIGN_OUT;
        public override List<(TypeInfo type, string propertyName)> PropertySequence => new()
        {
            (D_TYPE.TCHAR16, nameof(appStr)),
            (suf, nameof(suf)),
            (D_TYPE.DWORD, nameof(type))
        };

        protected override byte[] Write()
        {
            byte[] data = base.Write();
            byte[] extraBytes = Array.Empty<byte>();
            if (Code != null && Code.Length > 0)
            {
                extraBytes = Encoding.GetEncoding(DEFAULT_ENCODING_CODEPAGE).GetBytes(Code);
            }
          
            return GameUtility.CombineBytes(data, extraBytes);
        }
    }



    #endregion

    #region 收到的包
    public class OnBuildLinkPacket : ReceivePack
    {

    }

    public class OnGetAnonymousIDPacket : ReceivePack
    {
        public uint anonymousID;
        public uint lsListTimeVer;
        public uint serverTime;
        public uint stIpArea;

        public override List<(TypeInfo type, string propertyName)> PropertySequence => new()
        {
            (D_TYPE.DWORD, nameof(anonymousID)),
            (D_TYPE.DWORD, nameof(lsListTimeVer)),
            (D_TYPE.DWORD, nameof(serverTime)),
            (D_TYPE.DWORD, nameof(stIpArea))
        };
    }

    public class LsAddressInfo : ReceivePack
    {
        public uint privateIP;
        public int port;
        public string publicIP;
        public string host;

        public override List<(TypeInfo type, string propertyName)> PropertySequence => new()
        {
            (D_TYPE.DWORD, nameof(privateIP)),
            (D_TYPE.INT, nameof(port)),
            (D_TYPE.TCHAR16, nameof(publicIP)),
            (D_TYPE.TCHAR40, nameof(host))
        };
    }

    [Obsolete("不使用OnOldSocketLoginPacket，故UserInfo也无需使用")]
    public class UserInfo : ReceivePack
    {
        public override List<(TypeInfo type, string propertyName)> PropertySequence => new()
        {
            (D_TYPE.DWORD, nameof(uID)),
            (D_TYPE.DWORD, nameof(figureID)),
            (D_TYPE.DWORD, nameof(formID)),
            (D_TYPE.DWORD, nameof(sexID)),
            (D_TYPE.DWORD, nameof(userVip)),
            (D_TYPE.INT, nameof(money)),
            (D_TYPE.DWORD, nameof(coin)),
            (D_TYPE.DWORD, nameof(bonus)),
            (D_TYPE.DWORD, nameof(score)),
            (D_TYPE.DWORD, nameof(masterScore)),
            (D_TYPE.DWORD, nameof(matchCount)),
            (D_TYPE.DWORD, nameof(matchTime)),
            (D_TYPE.TCHAR64, nameof(userName)),
            (D_TYPE.TCHAR32, nameof(nickName)),
            (D_TYPE.TCHAR256, nameof(userface)),
            (D_TYPE.TCHAR32, nameof(realName)),
            (D_TYPE.TCHAR32, nameof(cardID))
        };

        /// <summary>
        /// 用户ID
        /// </summary>
        public string uID;

        /// <summary>
        /// 玩家头像ID
        /// </summary>
        public uint figureID;

        /// <summary>
        /// 游戏形象ID
        /// </summary>
        public uint formID;

        /// <summary>
        /// 性别ID
        /// </summary>
        public uint sexID;

        /// <summary>
        /// VIP会员标识
        /// </summary>
        public uint userVip;

        /// <summary>
        /// 银子数
        /// </summary>
        public int money;

        /// <summary>
        /// 元宝数
        /// </summary>
        public uint coin;

        /// <summary>
        /// 奖券数，显示时需要除以100后使用
        /// </summary>
        public uint bonus;

        /// <summary>
        /// 累计积分
        /// </summary>
        public uint score;

        /// <summary>
        /// 大师分
        /// </summary>
        public uint masterScore;

        /// <summary>
        /// 比赛盘数
        /// </summary>
        public uint matchCount;

        /// <summary>
        /// 比赛时长
        /// </summary>
        public uint matchTime;

        /// <summary>
        /// 账号名称
        /// </summary>
        public string userName;

        /// <summary>
        /// 用户昵称
        /// </summary>
        public string nickName;

        /// <summary>
        /// 用户自定义头像信息
        /// </summary>
        public string userface;

        /// <summary>
        /// 真实姓名（敏感信息）
        /// </summary>
        public string realName;

        /// <summary>
        /// 身份证号（敏感信息）
        /// </summary>
        public string cardID;
    }

    [Obsolete("请使用OnSocketLoginPacket，老包废弃")]
    public class OnOldSocketLoginPacket : ReceivePack
    {
        public LsAddressInfo lsAddrInfo = new LsAddressInfo();
        public UserInfo userInfo = new UserInfo();
        public string mobileNo;
        public string authentic;
        public uint devTrusted;
        public uint MID;
        public bool isNewUser;
        public LOGIN_JS_UPDATE_MODE jsUpdateMode;
        public uint dwParam3;
        public uint dwParam4;
        public SuffixIdxReceive suffixIdx = new SuffixIdxReceive();
        public uint suffixType;
        public uint count;

        /** 后缀数据，错误描述 */
        public string extraData = "";

        public override List<(TypeInfo type, string propertyName)> PropertySequence => new()
        {
            (lsAddrInfo, nameof(lsAddrInfo)),
            (userInfo, nameof(userInfo)),
            (D_TYPE.TCHAR16, nameof(mobileNo)),
            (D_TYPE.TCHAR64, nameof(authentic)),
            (D_TYPE.DWORD, nameof(devTrusted)),
            (D_TYPE.DWORD, nameof(MID)),
            (D_TYPE.DWORD, nameof(isNewUser)),
            (D_TYPE.DWORD, nameof(jsUpdateMode)),
            (D_TYPE.DWORD, nameof(dwParam3)),
            (D_TYPE.DWORD, nameof(dwParam4)),
            (suffixIdx, nameof(suffixIdx)),
            (D_TYPE.DWORD, nameof(suffixType)),
            (D_TYPE.DWORD, nameof(count))
        };

        public override ReceivePack Read(byte[] data)
        {
            base.Read(data);
            (int offset, int size) = ((int)(suffixIdx.offset - NewHeadReceive.Size), (int)suffixIdx.size);
            if (size != 0) 
            {
                var reader = new BinaryPacketReader(data[offset..(offset + size)]);
                extraData = reader.ReadString(size);
            }
            return this;
        }
    }

    public class OnSocketLoginPacket : ReceivePack
    {
        public LsAddressInfo lsAddrInfo = new LsAddressInfo();
        public uint uID;
        public string loginID;
        public string openID;
        public string userName;
        public string nickName;
        public string showName;
        public string mobileNo;
        public string authentic;
        public uint sexID;
        public uint userVip;
        public uint figureID;
        public int money;
        public uint coin;
        public uint bonus;
        public string userface;
        public string realName;
        public string cardID;
        public uint score;
        public uint masterScore;
        public uint matchCount;
        public uint matchTime;
        public uint isNewUser;
        public uint jsUpdateMode;
        /** 账号标记。在华为账号登录（LOGIN_TYPE.HARMONY_CREATE_ACCOUNT）时该值若为1则表示当前账号已注册过，不需要重复注册 */
        public uint accountFlag;
        public uint dwReserved2;
        public uint dwReserved3;
        public SuffixIdxReceive suffixIdx = new SuffixIdxReceive();
        /** 后缀数据，错误描述 */
        public string extraData = "";

        public override List<(TypeInfo type, string propertyName)> PropertySequence => new()
        {
            (lsAddrInfo, nameof(lsAddrInfo)),
            (D_TYPE.DWORD, nameof(uID)),
            (D_TYPE.TCHAR64, nameof(loginID)),
            (D_TYPE.TCHAR64, nameof(openID)),
            (D_TYPE.TCHAR64, nameof(userName)),
            (D_TYPE.TCHAR32, nameof(nickName)),
            (D_TYPE.TCHAR32, nameof(showName)),
            (D_TYPE.TCHAR16, nameof(mobileNo)),
            (D_TYPE.TCHAR64, nameof(authentic)),
            (D_TYPE.DWORD, nameof(sexID)),
            (D_TYPE.DWORD, nameof(userVip)),
            (D_TYPE.DWORD, nameof(figureID)),
            (D_TYPE.INT, nameof(money)),
            (D_TYPE.DWORD, nameof(coin)),
            (D_TYPE.DWORD, nameof(bonus)),
            (D_TYPE.TCHAR256, nameof(userface)),
            (D_TYPE.TCHAR32, nameof(realName)),
            (D_TYPE.TCHAR32, nameof(cardID)),
            (D_TYPE.DWORD, nameof(score)),
            (D_TYPE.DWORD, nameof(masterScore)),
            (D_TYPE.DWORD, nameof(matchCount)),
            (D_TYPE.DWORD, nameof(matchTime)),
            (D_TYPE.DWORD, nameof(isNewUser)),
            (D_TYPE.DWORD, nameof(jsUpdateMode)),
            (D_TYPE.DWORD, nameof(accountFlag)),
            (D_TYPE.DWORD, nameof(dwReserved2)),
            (D_TYPE.DWORD, nameof(dwReserved3)),
            (suffixIdx, nameof(suffixIdx))
        };

        public override ReceivePack Read(byte[] data)
        {
            base.Read(data);
            (int offset, int size) = ((int)(suffixIdx.offset - NewHeadReceive.Size), (int)suffixIdx.size);
            if (size != 0) 
            {
                var reader = new BinaryPacketReader(data[offset..(offset + size)]);
                extraData = reader.ReadString(size);
            }
            return this;
        }
    }

    public class OnSignOutPacket : ReceivePack
    {
        public uint anonymousID;
        public override List<(TypeInfo type, string propertyName)> PropertySequence => new()
        {
            (D_TYPE.DWORD, nameof(anonymousID))
        };
    }

    public class OnServiceErrorPacket : ReceivePack
    {
        public uint reason;
        public override List<(TypeInfo type, string propertyName)> PropertySequence => new()
        {
            (D_TYPE.DWORD, nameof(reason))
        };
    }

    public class OnWechatCodeAuthorizePacket : ReceivePack
    {
        public int retCode;
        public string openID;
        public string unionID;
        public override List<(TypeInfo type, string propertyName)> PropertySequence => new()
        {
            (D_TYPE.INT, nameof(retCode)),
            (D_TYPE.TCHAR64, nameof(openID)),
            (D_TYPE.TCHAR64, nameof(unionID))
        };
    }

    #endregion
}